# -*- coding: utf-8 -*-
"""merge_mixin
PDF 合并线程功能模块。
需要从 main.py 剪切以下方法到本类：
  * merge_pdfs_thread
  * _determine_target_rect
  * _perform_merge
"""

import os
import traceback
from tkinter import messagebox
import fitz  # PyMuPDF


class MergeMixin:
    """PDF 合并相关方法 mixin"""

    def merge_pdfs_thread(self, output_target, uniform_size, size_option, ref_pdf_idx_str):
        """执行PDF合并的核心功能的线程 (仅文件模式)"""
        try:
            # Removed is_folder_mode check and the entire if block for folder mode
            # if is_folder_mode:
            # ... FOLDER MODE LOGIC REMOVED ...
            # else:
            # --- 文件模式 (调用重构后的合并逻辑) ---
            merge_success, merge_error = self._perform_merge(
                pdf_list=self.merge_files,
                output_path=output_target,  # In file mode, output_target is the full output file path
                uniform_size=uniform_size,
                size_option=size_option,
                ref_pdf_idx_str=ref_pdf_idx_str,
                progress_callback=lambda p, msg: self.after(0, self.update_progress_safe, p, msg)
            )

            # --- 文件模式完成处理 ---
            if merge_success:
                file_count = len(self.merge_files)
                # 根据文件数量和是否统一尺寸，显示不同的成功消息
                if file_count == 1:
                    if uniform_size:
                        final_status = "尺寸统一完成"
                        success_message = (f"PDF页面尺寸统一成功！\n\n"
                                         f"已处理: {os.path.basename(self.merge_files[0])}\n"
                                         f"文件保存为: {output_target}")
                    else:
                        final_status = "处理完成"
                        success_message = (f"PDF处理成功！\n\n"
                                         f"已处理: {os.path.basename(self.merge_files[0])}\n"
                                         f"文件保存为: {output_target}")
                else:
                    final_status = "合并完成"
                    if uniform_size:
                        success_message = (f"PDF合并及尺寸统一成功！\n\n已合并 {file_count} 个文件\n"
                                         f"文件保存为: {output_target}")
                    else:
                        success_message = (f"PDF合并成功！\n\n已合并 {file_count} 个文件\n"
                                         f"文件保存为: {output_target}")

                self.after(0, self.update_progress_safe, 1.0, final_status)
                self.after(0, messagebox.showinfo, "完成", success_message)
            else:
                # 合并失败，显示错误
                self.after(0, messagebox.showerror, "错误", f"合并失败: {merge_error}")
                self.after(0, self.update_progress_safe, 0, "合并失败")


        except Exception as e:
            # 捕获线程级别的意外错误
            error_details = traceback.format_exc()
            self.after(0, self.show_error_message, f"合并过程中发生严重错误: {str(e)}\n{error_details}")
            self.after(0, self.update_progress_safe, 0, "合并失败")
        finally:
            # 确保UI在任何情况下都能重置
            self.after(0, self.reset_ui_state, self.merge_btn, self.merge_progress, self.merge_status, "准备就绪")

    def _determine_target_rect(self, pdf_list_for_sizing, size_option, ref_pdf_idx_str):
        """辅助函数：根据选项和PDF列表确定目标页面尺寸 (fitz.Rect)"""
        target_rect = None
        reference_pdf_index = -1  # 0-based

        if size_option == "A4标准":
            target_rect = fitz.Rect(0, 0, 595.276, 841.890)
        elif size_option == "第一页尺寸":
            if pdf_list_for_sizing:
                doc = fitz.open(pdf_list_for_sizing[0])
                target_rect = doc[0].rect
                doc.close()
                reference_pdf_index = 0
            else:
                raise ValueError("无法获取第一页尺寸，列表为空")
        elif size_option == "指定PDF尺寸":  # 注意：此选项在文件夹模式下不应被选择，但需处理以防万一
            if ref_pdf_idx_str and ref_pdf_idx_str != "-":
                try:
                    reference_pdf_index = int(ref_pdf_idx_str) - 1  # Convert to 0-based
                    if 0 <= reference_pdf_index < len(pdf_list_for_sizing):
                        doc = fitz.open(pdf_list_for_sizing[reference_pdf_index])
                        target_rect = doc[0].rect
                        doc.close()
                    else:
                        raise ValueError(f"指定的参考PDF索引 ({ref_pdf_idx_str}) 无效")
                except (ValueError, TypeError):
                    raise ValueError(f"无效的参考PDF索引: '{ref_pdf_idx_str}'")
            else:
                # 在文件模式下，如果未选择有效引用，则出错
                # 在文件夹模式下，此选项无效，不应到达这里，但如果到了，就视为错误
                raise ValueError("未选择有效的参考PDF (或在文件夹模式下使用了无效选项)")
        elif size_option == "自动适应最大":
            max_page_width = 0
            max_page_height = 0
            for file_path in pdf_list_for_sizing:
                try:
                    doc = fitz.open(file_path)
                    for page in doc:
                        rect = page.rect
                        max_page_width = max(max_page_width, rect.width)
                        max_page_height = max(max_page_height, rect.height)
                    doc.close()
                except Exception as e:
                    print(f"警告: 检查 '{os.path.basename(file_path)}' 尺寸时出错: {e}. 跳过此文件尺寸计算。")
                    # 不抛出错误，允许继续，但可能导致尺寸不准确
            if max_page_width > 0 and max_page_height > 0:
                target_rect = fitz.Rect(0, 0, max_page_width, max_page_height)
            else:
                raise ValueError("无法确定 '自动适应最大' 尺寸 (没有有效文件或页面?)")
        else:
            # 默认情况或未知选项 - 可以抛出错误或使用默认值（例如A4）
            print(f"警告: 未知的尺寸选项 '{size_option}', 使用A4尺寸。")
            target_rect = fitz.Rect(0, 0, 595.276, 841.890)
            # raise ValueError(f"未知的尺寸选项: {size_option}")

        return target_rect, reference_pdf_index

    def _perform_merge(self, pdf_list, output_path, uniform_size, size_option, ref_pdf_idx_str, progress_callback):
        """
        执行实际合并操作的内部函数 (PyMuPDF 或 PyPDF2)。
        返回 (success: bool, error_message: str | None)
        """
        total_files = len(pdf_list)
        failed_files_details = {}  # filename: error message

        try:
            if uniform_size:  # This means we ARE unifying size based on size_option
                is_get_pixmap_mode = False
                actual_size_option_for_determining_rect = size_option
                suffix_to_check = " (图像)"

                if size_option.endswith(suffix_to_check):
                    is_get_pixmap_mode = True
                    actual_size_option_for_determining_rect = size_option[:-len(suffix_to_check)]
                
                # --- 使用PyMuPDF合并并统一页面大小 ---
                merged_doc = fitz.open()
                target_rect = None
                reference_pdf_index = -1  # 0-based index for sizing reference

                # 1. 确定目标尺寸 (target_rect)
                try:
                    target_rect, reference_pdf_index = self._determine_target_rect(
                        pdf_list,
                        actual_size_option_for_determining_rect,
                        ref_pdf_idx_str
                    )
                except Exception as e:
                    if merged_doc and not merged_doc.is_closed: merged_doc.close()
                    return False, f"确定目标页面尺寸时出错: {e}"  # Abort early

                # 2. 遍历文件并添加/调整页面
                for i, file_path in enumerate(pdf_list):
                    file_name = os.path.basename(file_path)
                    current_progress = (i + 1) / total_files * 0.9
                    progress_callback(current_progress, f"处理文件: {file_name} ({i + 1}/{total_files})")

                    doc = None
                    try:
                        doc = fitz.open(file_path)

                        for page_num in range(doc.page_count):
                            # 所有页面都需要统一尺寸，不再跳过参考PDF
                            # 创建统一尺寸的新页面
                            new_page = merged_doc.new_page(width=target_rect.width, height=target_rect.height)
                            page_to_insert = doc[page_num]

                            if is_get_pixmap_mode:
                                # --- GET_PIXMAP LOGIC ---
                                source_bounds = page_to_insert.bound() # Use bounds for actual size after rotation

                                scale_w = target_rect.width / source_bounds.width if source_bounds.width > 0 else 1
                                scale_h = target_rect.height / source_bounds.height if source_bounds.height > 0 else 1
                                scale = min(scale_w, scale_h)

                                final_w = source_bounds.width * scale
                                final_h = source_bounds.height * scale
                                final_x = (target_rect.width - final_w) / 2
                                final_y = (target_rect.height - final_h) / 2

                                matrix = fitz.Matrix(scale, scale)
                                # The pretranslate part maps the top-left of the source_bounds (after scaling)
                                # to the final_x, final_y position on the target page.
                                matrix.pretranslate(final_x / scale - source_bounds.x0, final_y / scale - source_bounds.y0)

                                pix = None
                                try:
                                    # 降低DPI以减少内存占用（150 DPI通常已足够清晰）
                                    pix = page_to_insert.get_pixmap(matrix=matrix, dpi=150, alpha=False)
                                except MemoryError as mem_err:
                                    print(f"内存不足: 无法获取页面 {page_num + 1} 的Pixmap (文件: {file_name}): {mem_err}")
                                    print(f"建议: 使用不带'(图像)'的尺寸选项，或处理更少的页面")
                                except Exception as pix_err:
                                    print(f"警告: 无法获取页面 {page_num + 1} 的Pixmap (文件: {file_name}): {pix_err}")

                                if pix:
                                    new_page.insert_image(new_page.rect, pixmap=pix)
                                    pix = None  # Release memory
                                else:
                                    print(f"警告: 跳过插入页面 {page_num + 1} (文件: {file_name}) 因Pixmap获取失败。空白页已创建。")
                            else:
                                # --- SHOW_PDF_PAGE LOGIC (original) ---
                                source_rect = page_to_insert.rect # Original logic uses unrotated rect for scaling base
                                scale_w = target_rect.width / source_rect.width if source_rect.width > 0 else 1
                                scale_h = target_rect.height / source_rect.height if source_rect.height > 0 else 1
                                scale = min(scale_w, scale_h)
                                new_w = source_rect.width * scale
                                new_h = source_rect.height * scale
                                pos_x = (target_rect.width - new_w) / 2
                                pos_y = (target_rect.height - new_h) / 2
                                target_content_rect = fitz.Rect(pos_x, pos_y, pos_x + new_w, pos_y + new_h)
                                new_page.show_pdf_page(target_content_rect, doc, page_num, rotate=page_to_insert.rotation)
                        doc.close()
                        doc = None

                    except Exception as e:
                        print(f"处理文件 {file_name} 时出错: {e}")
                        failed_files_details[file_name] = str(e)
                        if doc and not doc.is_closed:
                            try: doc.close()
                            except: pass 
                        continue

                if len(merged_doc) > 0:
                    progress_callback(0.95, "正在保存文件...")
                    merged_doc.save(output_path, garbage=4, deflate=True, linear=False)
                else:
                    if merged_doc and not merged_doc.is_closed: merged_doc.close()
                    if not failed_files_details:
                        return False, "所有文件处理失败或为空，无法合并。"
                    else:
                        return False, f"没有页面被成功合并 (失败文件: {len(failed_files_details)})"
                
                if merged_doc and not merged_doc.is_closed: merged_doc.close()

            else:
                # --- 使用PyMuPDF进行简单合并 (替代原来的PyPDF2) ---
                merged_doc = fitz.open()  # Create a new empty PDF

                for i, file_path in enumerate(pdf_list):
                    file_name = os.path.basename(file_path)
                    current_progress = (i + 1) / total_files * 0.9
                    progress_callback(current_progress, f"处理文件: {file_name} ({i + 1}/{total_files})")

                    try:
                        # Open the source PDF
                        doc_to_add = fitz.open(file_path)
                        # Insert all pages from the source PDF into the merged PDF
                        # insert_pdf preserves original page properties including rotation
                        merged_doc.insert_pdf(doc_to_add)
                        doc_to_add.close()

                    except Exception as e:
                        print(f"PyMuPDF 处理文件 {file_name} 时出错: {e}")
                        failed_files_details[file_name] = f"读取/插入错误: {str(e)}"
                        # Try closing the doc if it was opened
                        if 'doc_to_add' in locals() and doc_to_add and not doc_to_add.is_closed:
                            try:
                                doc_to_add.close()
                            except:
                                pass
                        continue  # Skip to the next file

                # 保存 PyMuPDF 结果
                if len(merged_doc) > 0:
                    progress_callback(0.95, "正在保存文件...")
                    try:
                        merged_doc.save(output_path, garbage=4, deflate=True, linear=False)
                    except Exception as save_err:
                        if not merged_doc.is_closed: merged_doc.close()  # Close before raising
                        raise RuntimeError(f"保存合并后的PDF时出错: {save_err}")  # Propagate save error
                    finally:
                        # Ensure closure even on success
                        if not merged_doc.is_closed: merged_doc.close()
                else:
                    # Close the empty doc before returning failure
                    if not merged_doc.is_closed: merged_doc.close()
                    if not failed_files_details:
                        return False, "所有文件处理失败或为空，无法合并。"
                    else:
                        return False, f"没有页面被成功合并 (失败文件: {len(failed_files_details)})"

            # --- 合并成功 ---
            if failed_files_details:
                # 如果有部分文件失败，合并操作本身算成功，但返回错误信息以供上层报告
                error_msg = "部分文件处理失败:\n" + "\n".join(
                    [f"- {fname}: {emsg}" for fname, emsg in failed_files_details.items()])
                # 实际不应返回False，让外部判断。这里返回True，附带警告信息？
                # 暂时返回True，让外部处理警告。上层逻辑已处理failed_folders。
                print(f"合并完成，但有文件失败: {output_path}")  # Log warning
                # return True, error_msg # Alternative: return success but pass error details
            progress_callback(1.0, "合并完成")
            return True, None  # 完全成功

        except Exception as e:
            # 捕获此合并操作中的意外错误
            print(f"执行合并到 '{output_path}' 时发生严重错误: {e}")
            traceback.print_exc()
            return False, f"合并操作内部错误: {str(e)}"


